#pragma once

// 我们自己实现序列化与反序列化

#include "Logger.hpp"

#include <iostream>
#include <string>
#include <unordered_map> //存放 k-V结构的数据
#include <sstream>
#include <unordered_map>
#include <unistd.h>
#include <fstream>
#include <functional>
#include <strings.h>

static const std::string linesep = "\r\n"; // 行分隔符
static const std::string innersep1 = " ";
static const std::string innersep2 = ": ";
static const std::string webroot = "./wwwroot";
static const std::string defaulthome = "index.html";
static const std::string html_404 = "404.html";
static const std::string suffixsep = ".";
static const std::string argssep = "?";

// 请求：
// 方法 url 版本 \r\n
// K: V\r\n
//  \r\n
// 正文（可以没有）
class HttpRequest
{
private:
    // 增加一个标志位来标识是读到了空行还是没有找到 \r\n
    std::string ReadOneLine(std::string &reqstr, bool *status) // 获取第一行并处理reqstr
    {
        // 查找分隔符 \r\n
        auto pos = reqstr.find(linesep);
        if (pos == std::string::npos)
        {
            *status = false;
            return std::string();
        }
        // 切割
        *status = true;
        std::string line = reqstr.substr(0, pos);
        // 对reqstr 进行处理
        reqstr.erase(0, pos + linesep.size());
        return line;
    }
    // 对第一行进行解析
    void ParseReqLine(std::string &reqline)
    {
        std::stringstream ss(reqline);
        ss >> _method >> _uri >> _httpversion;
    }
    void BuildKV(std::string &reqline, std::string *k, std::string *v)
    {
        // 以 innersep2 作为分隔符
        auto pos = reqline.find(innersep2);
        if (pos == std::string::npos)
        {
            *k = std::string();
            *v = std::string();
            return;
        }
        *k = reqline.substr(0, pos);
        *v = reqline.substr(pos + innersep2.size());
    }

public:
    HttpRequest() {}
    ~HttpRequest() {}
    // 应答由服务器进行反序列话，序列化的工作由浏览器来做
    // 反序列化：拆分字符串
    bool Deserialize(std::string &requeststr)
    {
        bool status = true;

        // 打印请求--debug
        std::cout << requeststr << std::endl;

        // 提取一行，并解析第一行
        std::string reqline = ReadOneLine(requeststr, &status);
        if (status == false)
            return false;
        // 对第一行进行解析
        ParseReqLine(reqline);

        // LOG(LogLevel::DEBUG) << "请求第一行： " << _method << " " << _uri << " " << _httpversion;

        // 对报头的剩余部分进行处理；当读到空行报文就结束了，剩余的在requsetstr中
        while (true)
        {
            status = true;
            reqline = ReadOneLine(requeststr, &status);
            // 不空 真->读到有效行
            // 空 真 -> 读到空行，报头 的最后一行
            // 空 假 --> 读取结束
            if (status && !reqline.empty())
            {
                // 解析 K-V结构
                std::string k, v;
                BuildKV(reqline, &k, &v);
                if (k.empty() || v.empty())
                    continue;

                _req_handers.insert(std::make_pair(k, v));
            }
            else if (status)
            {
                // 读取到空行
                _blank_line = linesep;
                break;
            }
            else
            {
                // 读取错误
                LOG(LogLevel::WARNING) << "读取错误";
                // return false;
                break;
            }
        }

        // for (auto &str : _req_handers)
        // {
        //     LOG(LogLevel::DEBUG) << str.first << "## " << str.second;
        // }

        // 获取访问的资源路径
        _path = webroot;
        if (_uri == "/")
        {
            _path += _uri;        //./wwwroot/
            _path += defaulthome; // ./wwwroot/index.html
        }
        else
        {
            _path += _uri;
        }

        // 记录访问者的主机信息
        // std::cout << "请求主机: " << _req_handers["User-Agent"] << std::endl;

        // 当为login 等模块，GET方法：提取uri 中的数据  -> 判断uri 中是否有 ？
        // GET:s数据信息放在uri 中，POST：数据信息放在正文，此处需要判断方法是GET还是POST
        //  auto pos = _path.find(argssep);
        //  LOG(LogLevel::DEBUG) << " path : " << _path;
        //  if(pos != std::string::npos)
        //  {
        //      _args = _path.substr(pos+argssep.size());//数据信息
        //      _path = _path.substr(0 , pos);
        //  }
        //  //debug
        //  LOG(LogLevel::DEBUG) << " path : " << _path;
        //  LOG(LogLevel::DEBUG) << " args : " << _args;

        // 大小写忽略：strcasecmp
        if (strcasecmp(_method.c_str(), "GET") == 0)
        {
            // GET方法
            auto pos = _path.find(argssep);
            LOG(LogLevel::DEBUG) << " path : " << _path;
            if (pos != std::string::npos)
            {
                _req_body = _path.substr(pos + argssep.size()); // 数据信息
                _path = _path.substr(0, pos);
            }
            // debug
            LOG(LogLevel::DEBUG) << " path : " << _path;
            LOG(LogLevel::DEBUG) << " args : " << _req_body;
        }
        else if(strcasecmp(_method.c_str() , "POST") == 0)
        {
            // _args = _req_body;
                   // 正文部分
            _req_body = requeststr; // 正文有就有，没有就没有
        }

        //其实可以不用多创建一个变量 _args , 使用 _req_body 也是可以的

        return true;
    }
    // 提供Get函数
    std::string Path()
    {
        return _path;
    }

    // std::string Args()
    // {
    //     return _args;
    // }
    std::string Body()
    {
        return _req_body;
    }

    // 获取资源文件的后缀
    std::string Suffix()
    {
        if (_path.empty()) // 没有获取资源文件返回空字符串
        {
            return std::string();
        }
        // path 中存放的就是资源路径，利用rfind 从后往前找 .
        auto pos = _path.rfind(suffixsep);
        if (pos == std::string::npos)
        {
            return std::string();
        }
        // 截取返回
        return _path.substr(pos); //.html
    }

    void SetPath(const std::string &path)
    {
        _path = path;
    }

private:
    std::string _method;
    std::string _uri;
    std::string _httpversion;
    std::unordered_map<std::string, std::string> _req_handers;
    std::string _blank_line;
    std::string _req_body;

    // 请求访问的资源路径
    std::string _path;
    // std::string _args; // 暂时这样处理
};

// 应答
// 版本 状态码 状态码描述 \r\n
// K: V\r\n
//\r\n
// 正文（可以没有）
class HttpResponce
{
private:
    // 建立状态码与状态描述的映射关系
    std::string Code2Desc(int code)
    {
        switch (code)
        {
        case 200:
            return "OK";
        case 404:
            return "Not Found";
        case 400:
            return "Bad Request";
        case 301:
            return "Moved Permanently";
        case 302:
            return "See Other";
        case 307:
            return "Temporary Redirect";
        default:
            return "";
        }
    }

public:
    HttpResponce() : _httpversion("HTTP/1.1"), _blank_line("\r\n")
    {
    }
    ~HttpResponce() {}
    // 应答增加设置报头属性的功能
    bool SetHeader(const std::string &key, const std::string &value)
    {
        _resp_handers[key] = value;
        return true;
    }

    // 应答的序列化工作由服务器来做，应答反序列化的工作由浏览器做
    // 序列化：拼接字符串返回
    std::string Serialize()
    {
        std::string respstr = _httpversion + innersep1 + std::to_string(_code) + innersep1 + _desc + linesep;

        // 增加报头属性，暂时添加 Content-Length --> 自描述，正文的长度
        if (!_resp_body.empty())
        {
            std::string len = std::to_string(_resp_body.size());
            SetHeader("Content-Length", len);
        }

        // 处理KV结构
        for (auto &elem : _resp_handers)
        {
            std::string line = elem.first + innersep2 + elem.second + linesep;
            respstr += line;
        }
        respstr += _blank_line; // 加上空行
        respstr += _resp_body;  // 加上正文

        return respstr;
    }

    // void ReadContent(const std::string &path)
    // {
    //     //将文件中的内容读到内存中
    //     //以二进制的形式打开
    //     std::ifstream file(path , std::ios::binary);
    //     if(!file.is_open())
    //     {
    //         // exit(0);
    //         std :: cerr << "打开资源文件失败";
    //     }
    //     LOG(LogLevel::DEBUG) << "请求访问的资源文件已打开";

    //     //定位到文件末尾获取文件的大小
    //     file.seekg(0 ,std::ios::end);
    //     std::streampos filesize = file.tellg();
    //     file.seekg(0 , std::ios::beg);

    //      _resp_body.resize(static_cast<size_t>(filesize));

    //      //读取文件中内容到字符串
    //      file.read(&_resp_body[0],static_cast<std::streamsize>(filesize));
    //      if(!file)
    //      {
    //        std::cerr << "读取文件时出错" <<std::endl;
    //      }
    //      //debug
    //      std::cout << _resp_body <<std::endl;

    //      file.close();
    // }

    // 二进制的形式读取资源文件
    bool ReadContent(const std::string &path) // 对于http所有的请求均会有应答，次函数需设置 返回值
    {
        std::ifstream file(path, std::ios::binary);
        if (!file.is_open())
        {
            std::cerr << "打开资源文件失败：" << path << std::endl;
            return false;
        }
        // LOG(LogLevel::DEBUG) << "请求访问的资源文件已打开";

        file.seekg(0, std::ios::end);
        std::streampos filesize = file.tellg();
        file.seekg(0, std::ios::beg);

        if (filesize < 0)
        {
            std::cerr << "获取文件大小失败" << std::endl;
            return false;
        }

        // 检查文件大小是否超过string的最大大小
        if (static_cast<size_t>(filesize) > _resp_body.max_size())
        {
            std::cerr << "文件太大，无法读取" << std::endl;
            return false;
        }

        size_t size = static_cast<size_t>(filesize);
        _resp_body.resize(size);

        file.read(&_resp_body[0], static_cast<std::streamsize>(filesize));

        if (!file)
        {
            std::cerr << "读取文件时出错，实际读取了 " << file.gcount() << " 字节" << std::endl;
            _resp_body.resize(static_cast<size_t>(file.gcount()));
        }

        // std::cout << _resp_body << std::endl;

        file.close();
        return true;
    }

    // 暂时的方法来处理状态码和状态码描述
    void SetCode(int code)
    {
        if (code >= 100 && code < 600)
        {
            _code = code;
            // 利用状态码与状态描述的映射关系来初始化状态码描述
            _desc = Code2Desc(code);
        }
        else
        {
            LOG(LogLevel::DEBUG) << "非法状态码: " << code;
        }
    }
    // debug
    std::string GetBody()
    {
        return _resp_body;
    }

private:
    std::string _httpversion;
    int _code;
    std::string _desc; // 状态码描述
    std::unordered_map<std::string, std::string> _resp_handers;
    std::string _blank_line;
    std::string _resp_body;
};

using func_t = std::function<HttpResponce(HttpRequest &)>; // 请求作为参数，返回应答

class Http
{
private:
    // 功能路由
    std::unordered_map<std::string, func_t> _handlers;

public:
    // 构建应答填充属性：Content-Type 正文的数据类型；Content-Type 对照表中收集了所有文件类型；可以设置配置文件
    // 然后加载进来，此处简单处理，枚举几个常用的文件类型
    std::string Suffix2Desc(const std::string &suffix)
    {
        LOG(LogLevel::DEBUG) << "suffix: " << suffix;
        if (suffix == ".html")
            return "text/html";
        else if (suffix == ".css")
            return "text/css";
        else if (suffix == ".js")
            return "application/javascrip";
        else if (suffix == ".png")
            return "image/png";
        else if (suffix == ".jpg")
            return "image/jpeg";
        else if (suffix == ".txt")
            return "text/plain";
        else
            return "text/html";
    }

public:
    Http() {}
    ~Http() {}

    // 方法注册
    /// login -> ./wwwroot/login
    void Register(const std::string &action, func_t handler)
    {
        std::string key = webroot;
        key += action;
        _handlers[key] = handler;
    }
    // 统筹整个反序列化、处理、序列化的过程
    std::string HandlerRequest(std::string &requeststr)
    {
        std::string respstr;
        // LOG(LogLevel::DEBUG) << "请求：" << respstr;

        HttpRequest req;
        int r = req.Deserialize(requeststr);
        // LOG(LogLevel::DEBUG) << "请求反序列化是否成功；" << r;

        if (r)
        {
            // 反序列化成功就处理请求
            // uri 是客户向服务器请求的资源路径，我们需要自己处理、拼接
            HttpResponce resp;
            // 应答获取数据
            std::string target = req.Path();
            // LOG(LogLevel::DEBUG) << "请求的资源路径：" << req.Path();
            if (_handlers.count(target)) // 动态交互功能模块，交给上层处理
            {
                // 功能路由
                resp = _handlers[target](req);
            }
            else // 静态网页处理
            {
                if (resp.ReadContent(req.Path()))
                {
                    // 设置应答中的Content-Type
                    std::string suffix = req.Suffix();
                    std::string min_type_value = Suffix2Desc(suffix);
                    resp.SetHeader("Content-Type", min_type_value);
                    // 获取资源成功就设置ok的状态码
                    resp.SetCode(200);
                }
                else // 优化：读取文件失败，用重定向来进行跳转
                {
                    // 打开文件失败 404
                    // std::string err_404 = webroot + "/" + html_404;
                    // //设置Content-Type,将其资源路径写入request 的_path 中
                    // req.SetPath(err_404);
                    // // 读取404.html 中的数据
                    // resp.ReadContent(req.Path());

                    // std::string suffix = req.Suffix();
                    // std::string min_type_value = Suffix2Desc(suffix);
                    // resp.SetHeader("Content-Type" , min_type_value);
                    // resp.SetCode(404);

                    // 重定向
                    resp.SetCode(302); // 临时重定向
                    resp.SetHeader("Location", "/404.html");
                }
            }

            // LOG(LogLevel::DEBUG) << "应答正文；" << resp.GetBody();

            // 将应答序列化
            respstr = resp.Serialize();
            // debug
            // LOG(LogLevel::DEBUG) << "应答：" << respstr;
            // std::cout << respstr << std::endl;
        }
        return respstr;
    }
    // private:
};